/**
 * @Author: your name
 * @Date: 2023-08-21 14:45:20
 * @LastEditTime: 2023-08-22 14:15:23
 * @LastEditors: your name
 * @Description: ''
 * @FilePath: \vue2-element-admin-master-diy\mock\mock-server.js
 */

// chokidar 是一个用于监听文件变化的库，可以监视文件或文件夹的添加、修改和删除等操作。
const chokidar = require('chokidar')
// body-parser 是一个中间件，用于解析 HTTP 请求体中的数据。
const bodyParser = require('body-parser')
// chalk 是一个用于在命令行界面中着色输出的库，可以用于美化命令行输出的样式。
const chalk = require('chalk')
// path 是一个内置模块，用于处理文件路径和目录路径的实用工具。
const path = require('path')
// mockjs 是一个用于生成随机数据和拦截 Ajax 请求的库，用于前端开发中的模拟数据。
const Mock = require('mockjs')
/**
 * 这段代码是使用 Node.js 的 path 模块和 process 对象来获取当前工作目录，并将其与 'mock' 目录名连接起来，生成一个完整的路径。
 * 首先，path.join() 方法被调用，传入了两个参数：process.cwd() 和 'mock'。process.cwd() 返回当前 Node.js 进程的当前工作目录。
 * 然后，path.join() 方法将这两个路径片段连接起来，生成一个完整的路径字符串。使用 path.join() 方法可以确保在不同操作系统上正确地构建有效的路径。
 * 最后，这个完整的路径字符串被赋值给 mockDir 变量，供后续的代码使用。
 * 例如，如果当前工作目录是 /home/user/project，那么执行该代码后，mockDir 的值将成为 /home/user/project/mock。
 * 这种方法可以用于构建需要指向项目中 'mock' 目录的路径，以便在项目中进行模拟数据相关的操作。
 */
const mockDir = path.join(process.cwd(), 'mock')

function registerRoutes(app) {
  let mockLastIndex
  const { mocks } = require('./index.js')
  const mocksForServer = mocks.map((route) => {
    return responseFake(route.url, route.type, route.response)
  })
  for (const mock of mocksForServer) {
    app[mock.type](mock.url, mock.response)
    mockLastIndex = app._router.stack.length
  }
  const mockRoutesLength = Object.keys(mocksForServer).length
  return {
    mockRoutesLength: mockRoutesLength,
    mockStartIndex: mockLastIndex - mockRoutesLength
  }
}

function unregisterRoutes() {
  Object.keys(require.cache).forEach((i) => {
    if (i.includes(mockDir)) {
      delete require.cache[require.resolve(i)]
    }
  })
}

// for mock server
const responseFake = (url, type, respond) => {
  return {
    url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`),
    type: type || 'get',
    response(req, res) {
      console.log('request invoke:' + req.path)
      res.json(
        Mock.mock(respond instanceof Function ? respond(req, res) : respond)
      )
    }
  }
}

module.exports = (app) => {
  // parse app.body
  // https://expressjs.com/en/4x/api.html#req.body
  app.use(bodyParser.json())
  app.use(
    bodyParser.urlencoded({
      extended: true
    })
  )

  const mockRoutes = registerRoutes(app)
  var mockRoutesLength = mockRoutes.mockRoutesLength
  var mockStartIndex = mockRoutes.mockStartIndex

  // watch files, hot reload mock server
  chokidar
    .watch(mockDir, {
      ignored: /mock-server/,
      ignoreInitial: true
    })
    .on('all', (event, path) => {
      if (event === 'change' || event === 'add') {
        try {
          // remove mock routes stack
          app._router.stack.splice(mockStartIndex, mockRoutesLength)

          // clear routes cache
          unregisterRoutes()

          const mockRoutes = registerRoutes(app)
          mockRoutesLength = mockRoutes.mockRoutesLength
          mockStartIndex = mockRoutes.mockStartIndex

          console.log(
            chalk.magentaBright(
              `\n > Mock Server hot reload success! changed  ${path}`
            )
          )
        } catch (error) {
          console.log(chalk.redBright(error))
        }
      }
    })
}
